﻿using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Com.Lancetinc.Evs.Data
{
    public class CrudRepository<TEntity> : QueryRepository<TEntity> where TEntity : class
    {
        private string _insertSql;

        public CrudRepository(params string[] keyColumnNames)
            : base(keyColumnNames)
        {
        }

        public void Insert(DbCommand cmd, TEntity entity)
        {
            Insert(cmd, new[] { entity });
        }

        public void Insert(DbCommand cmd, IEnumerable<TEntity> entities)
        {
            cmd.CommandText = GetInsertSql();
            foreach (TEntity entity in entities)
            {
                cmd.Parameters.Clear();
                foreach (PropertyInfo column in Columns)
                {
                    AddDbParameter(cmd, NormalizeParamName(GetColumnName(column)), column.GetValue(entity, null));
                }
                cmd.ExecuteNonQuery();
            }
        }

        public int UpdateByKey(DbCommand cmd, TEntity entity, Predicate<string> columnFilter = null)
        {
            var updateColumnValue = new Dictionary<string, object>();
            var whereCondItems = new List<SqlCondItem>();
            foreach (var column in Columns)
            {
                if (KeyColumns.Contains(column))
                {
                    whereCondItems.Add(new SqlCondItem(column.Name, column.GetValue(entity, null)));
                }
                else
                {
                    if (columnFilter != null && !columnFilter.Invoke(column.Name))
                        continue;
                    updateColumnValue.Add(column.Name, column.GetValue(entity, null));
                }
            }
            return Update(cmd, updateColumnValue, whereCondItems.ToArray());
        }

        protected int Update(DbCommand cmd, Dictionary<string, object> updateColumnValue, SqlCondItem[] whereCondItems)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("update ").Append(NormalizeTableName(TableName)).Append(" set ");
            bool first = true;
            foreach (var columnName in updateColumnValue.Keys)
            {
                if (first)
                {
                    first = false;
                }
                else
                {
                    sb.Append(",");
                }
                var columnParam = NormalizeParamName(columnName);
                sb.Append(NormalizeColumnName(columnName)).Append("=").Append(NormalizeParamName(columnName));
                AddDbParameter(cmd, columnParam, updateColumnValue[columnName]);
            }
            sb.Append(AddWhere(cmd, whereCondItems));
            cmd.CommandText = sb.ToString();
            return cmd.ExecuteNonQuery();
        }

        protected int Delete(DbCommand cmd, SqlCondItem[] whereCondItems)
        {
            StringBuilder sb = new StringBuilder("delete from ").Append(NormalizeTableName(TableName));
            sb.Append(AddWhere(cmd, whereCondItems));
            cmd.CommandText = sb.ToString();
            return cmd.ExecuteNonQuery();
        }

        protected int DeleteByKey(DbCommand cmd, params object[] values)
        {
            return Delete(cmd, ToKeyWhereCondItems(values));
        }

        protected string GetInsertSql()
        {
            if (_insertSql == null)
            {
                var columnNames = Columns.Select(x => GetColumnName(x)).ToArray();

                StringBuilder sb = new StringBuilder("insert into");
                sb.Append(NormalizeTableName(TableName)).Append("(");
                sb.Append(string.Join(",", columnNames.Select(x => NormalizeColumnName(x))));
                sb.Append(") values (");
                sb.Append(string.Join(",", columnNames.Select(x => NormalizeParamName(x))));
                sb.Append(")");
                _insertSql = sb.ToString();
            }
            return _insertSql;
        }
    }
}
